package cn.changeforyou.web.cloud.devUtilApi.modules.sql.impl;

import cn.changeforyou.utils.string.StringUtils;
import cn.changeforyou.web.cloud.devUtilApi.db.sql.JdbcJavaTypeConvertor;
import cn.changeforyou.web.cloud.devUtilApi.parser.java.JavaEntity;
import cn.changeforyou.web.cloud.devUtilApi.parser.java.JavaEntityField;
import cn.changeforyou.web.utils.NameUtils;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.ReceiverParameter;
import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import lombok.extern.slf4j.Slf4j;

/**
 * javaParser lib的工具类
 *
 * @author zhyu
 * @version 1.0
 * @date 2022/4/12 9:37
 */

@Slf4j
public class JavaParserUtil {

    public static final WriteJavaEntityContext default_writeJavaEntityContext = new WriteJavaEntityContext();

    static {
        default_writeJavaEntityContext.setTimeType(TimeType.java8TimeApi);
        default_writeJavaEntityContext.setLombok(false);
    }

    /**
     * 把javaEntity写成字符串
     *
     * @param javaEntity
     * @param context
     * @return
     */
    public static String writeJavaEntityString(JavaEntity javaEntity, WriteJavaEntityContext context) {
        if (null == context) {
            context = default_writeJavaEntityContext;
        }
        CompilationUnit compilationUnit = new CompilationUnit();
        if (StrUtil.isNotBlank(javaEntity.getPackageName())) {
            compilationUnit.setPackageDeclaration(javaEntity.getPackageName());
        } else {
            compilationUnit.setPackageDeclaration("xx.xxx");
        }
        ClassOrInterfaceDeclaration classDeclaration = compilationUnit.addClass(NameUtils.getFirstUpperName(javaEntity.getName()));
        String entityComment = javaEntity.getComment();

        if (context.isLombok()) {
            //lombok打包不进去
            classDeclaration.addMarkerAnnotation(lombok.Data.class);
        }

        if (StringUtils.isNotBlank(entityComment)) {
            JavadocComment javadocComment = new JavadocComment();
            javadocComment.setContent(entityComment);
            classDeclaration.setJavadocComment(javadocComment);
        }

        SerializationType serializationType = context.getSerializationType();
        for (JavaEntityField entityField : javaEntity.getJavaEntityFields()) {
            String name = entityField.getName();
            boolean useAlias = false;
            switch (serializationType) {
                case origin:
                    entityField.setUseName(name);
                    break;
                case gson:
                case jackson:
                case fastjson2:
                case allCamelCase: {
                    String useName = StrUtil.toCamelCase(name);
                    entityField.setUseName(NameUtils.getFirstLowerName(useName));
                    if (!useName.equals(name)) {
                        useAlias = true;
                    }
                    break;
                }
                case allUnderline:
                    String useName = StrUtil.toUnderlineCase(name);
                    entityField.setUseName(NameUtils.getFirstLowerName(useName));
                    if (!useName.equals(name)) {
                        useAlias = true;
                    }
                    break;
            }

            Class javaClass = entityField.getJavaClass();
            if (null == javaClass) {
                javaClass = JdbcJavaTypeConvertor.getJavaTypeByDataTypeAndWriteEntityContext(entityField.getDataType(), context);
                entityField.setJavaClass(javaClass);
            }
            String comment = entityField.getComment();

            FieldDeclaration fieldDeclaration = classDeclaration.addField(javaClass, entityField.getUseName(), Modifier.Keyword.PRIVATE);

            if (StringUtils.isNotBlank(comment)) {
                JavadocComment javadocComment = new JavadocComment();
                javadocComment.setContent(entityComment);
                fieldDeclaration.setJavadocComment(comment);
            }
            if (useAlias) {
                switch (serializationType) {
                    case jackson:
                        fieldDeclaration.addSingleMemberAnnotation(JsonProperty.class, "\"" + entityField.getName() + "\"");
                        break;
                    case gson:
                        fieldDeclaration.addSingleMemberAnnotation(com.google.gson.annotations.SerializedName.class, "\"" + entityField.getName() + "\"");
                        break;
                    case fastjson2:
                        NormalAnnotationExpr jsonFieldAnnotation = fieldDeclaration.addAndGetAnnotation(com.alibaba.fastjson2.annotation.JSONField.class);
                        jsonFieldAnnotation.addPair("name", "\"" + entityField.getName() + "\"");
                        break;
                }
            }
        }

        if (!context.isLombok()) {
            for (JavaEntityField entityField : javaEntity.getJavaEntityFields()) {
                String useName = entityField.getUseName();
                Class javaClass = entityField.getJavaClass();
                MethodDeclaration getMethod = classDeclaration.addMethod("get" + NameUtils.getFirstUpperName(useName), Modifier.Keyword.PUBLIC);
                ReturnStmt returnStmt = new ReturnStmt(new NameExpr("this." + useName));
                getMethod.setBody(new BlockStmt().addStatement(returnStmt));
                getMethod.setType(javaClass);

                MethodDeclaration setMethod = classDeclaration.addMethod("set" + NameUtils.getFirstUpperName(useName), Modifier.Keyword.PUBLIC);
                ReceiverParameter receiverParameter = new ReceiverParameter();
                receiverParameter.setType(javaClass);
                setMethod.setReceiverParameter(receiverParameter);
                setMethod.setBody(new BlockStmt().addStatement("this." + useName + " = " + useName + ";"));

                if (log.isDebugEnabled()) {
                    log.debug("已添加完字段:{}", entityField.getName());
                }
            }
        }

        String result = compilationUnit.toString();
        return result;
    }
}
